home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / aztecnos.arc / FTPCLI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1989-03-13  |  15.6 KB  |  688 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "global.h"
  4. #include "mbuf.h"
  5. #include "session.h"
  6. #include "cmdparse.h"
  7. #include "proc.h"
  8. #include "tty.h"
  9. #include "socket.h"
  10. #include "ftp.h"
  11. #include "ftpcli.h"
  12.  
  13. static int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),
  14.     domkdir(),dormdir(),doascii(),dobinary(),doquit();
  15. static int getsub();
  16. static void sendport();
  17. struct mbuf *getline();
  18. int getresp(); 
  19.  
  20. extern struct session *Current;
  21. extern char Nospace[],Badhost[];
  22. extern char *Tcpstates[],*Reasons[],*Unreach[],*Exceed[];
  23. extern int32 Clock;
  24. static char Notsess[] = "Not an FTP session!\n";
  25. static char Cantwrite[] = "Can't write %s\n";
  26. static char Cantread[] = "Can't read %s\n";
  27.  
  28. struct cmds Ftpcmds[] = {
  29.     "",        donothing,    0, 0, NULLCHAR,
  30.     "ascii",    doascii,    0, 0, NULLCHAR,
  31.     "binary",    dobinary,    0, 0, NULLCHAR,
  32.     "cd",        doftpcd,    0, 2, "cd <directory>",
  33.     "dir",        dolist,        0, 0, NULLCHAR,
  34.     "list",        dolist,        0, 0, NULLCHAR,
  35.     "get",        doget,        0, 2, "get remotefile <localfile>",
  36.     "ls",        dols,        0, 0, NULLCHAR,
  37.     "mkdir",    domkdir,    0, 2, "mkdir <directory>",
  38.     "nlst",        dols,        0, 0, NULLCHAR,
  39.     "quit",        doquit,        0, 0, NULLCHAR,
  40.     "rmdir",    dormdir,    0, 2, "rmdir <directory>",
  41.     "put",        doput,        0, 2, "put localfile <remotefile>",
  42.     "type",        dotype,        0, 0, NULLCHAR,
  43.     NULLCHAR,    NULLFP,        0, 0, NULLCHAR,
  44. };
  45.  
  46. /* Handle top-level FTP command */
  47. doftp(argc,argv)
  48. int argc;
  49. char *argv[];
  50. {
  51.     struct session *sp;
  52.     struct ftpcli ftp;
  53.     struct sockaddr_in fsocket;
  54.     int resp;
  55.     struct mbuf *bp,*bpsav;
  56.     char *cp;
  57.     int control;
  58.  
  59.     /* Allocate a session control block */
  60.     if((sp = newsession(argv[1],FTP)) == NULLSESSION){
  61.         printf("Too many sessions\n");
  62.         return 1;
  63.     }
  64.     memset((char *)&ftp,0,sizeof(ftp));
  65.     ftp.control = ftp.data = -1;
  66.  
  67.     sp->cb.ftp = &ftp;    /* Downward link */
  68.     ftp.session = sp;    /* Upward link */
  69.     ftp.output = Curproc;
  70.  
  71.     fsocket.sin_family = AF_INET;
  72.     if(argc < 3)
  73.         fsocket.sin_port = IPPORT_FTP;
  74.     else
  75.         fsocket.sin_port = atoi(argv[2]);
  76.  
  77.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  78.         printf(Badhost,sp->name);
  79.         freesession(sp);
  80.         return 1;
  81.     }
  82.     /* Open the control connection */
  83.     control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0);
  84.     printf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  85.     if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
  86.         goto quit;
  87.     printf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
  88.         sp->name);
  89.  
  90.     /* Wait for greeting from server */
  91.     resp = getresp(control,200);
  92.  
  93.     /* Now process responses and commands */
  94.     for(;;){
  95.         switch(resp){
  96.         case -1:
  97.             goto quit;
  98.         case 220: /* Sign-on banner; prompt for and send USER command */
  99.             printf("Enter user name: ");
  100.             bp = qdata("USER ",5);
  101.             append(&bp,getline(sp));
  102.             if(send_mbuf(control,bp,0,NULLCHAR,0) == -1)
  103.                 goto quit;
  104.             resp = getresp(control,200);
  105.             break;
  106.         case 331: /* Password prompt; get password */
  107.             printf("Password: ");
  108.             ttysetmode(TTY_EDIT);    /* turn off echo */
  109.             bp = qdata("PASS ",5);
  110.             append(&bp,getline(sp));
  111.             printf("\r\n");
  112.             if(send_mbuf(control,bp,0,NULLCHAR,0) == -1)
  113.                 goto quit;
  114.             ttysetmode(TTY_EDIT|TTY_ECHO);
  115.             resp = getresp(control,200);
  116.             break;
  117.         default:
  118.             /* Test the control channel first */
  119.             if(sockstate(control) == NULLCHAR)
  120.                 goto quit;
  121.  
  122.             printf("ftp> ");
  123.             bp = getline(sp);
  124.  
  125.             /* Copy because cmdparse modifies the original */
  126.             bpsav = copy_p(bp,len_mbuf(bp));
  127.             if((resp = cmdparse(Ftpcmds,bp->data)) != -1){
  128.                 /* Valid command, free buffer and get another */
  129.                 free_p(bpsav);
  130.             } else {
  131.                 /* Not a local cmd, send to remote server */
  132.                 if(send_mbuf(control,bpsav,0,NULLCHAR,0) == -1){
  133.                     free_p(bp);
  134.                     goto quit;
  135.                 }
  136.                 resp = getresp(control,200);
  137.             }
  138.             free_p(bp);
  139.             break;
  140.         }
  141.     }
  142. quit:    cp = sockerr(control);
  143.     printf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
  144.      cp != NULLCHAR ? cp : "EOF");
  145.  
  146.     if(ftp.fp != NULLFILE && ftp.fp != stdout)
  147.         fclose(ftp.fp);
  148.     if(ftp.data != -1)
  149.         close_s(ftp.data);
  150.     if(ftp.control != -1)
  151.         close_s(ftp.control);
  152.     if(ftp.session != NULLSESSION)
  153.         freesession(ftp.session);
  154.     return 0;
  155. }
  156.  
  157. /* Handle null line to avoid trapping on first command in table */
  158. static
  159. int
  160. donothing(argc,argv)
  161. int argc;
  162. char *argv[];
  163. {
  164. }
  165. /* Close session */
  166. static int
  167. doquit(argc,argv)
  168. int argc;
  169. char *argv[];
  170. {
  171.     register struct ftpcli *ftp;
  172.     int control;
  173.  
  174.     ftp = Current->cb.ftp;
  175.     control = ftp->control;
  176.     usprintf(control,"QUIT\r\n");
  177.     getresp(control,200);    /* Get the closing message */
  178.     getresp(control,200);    /* Wait for the server to close */
  179. }
  180.  
  181. /* Translate 'cd' to 'cwd' for convenience */
  182. static
  183. int
  184. doftpcd(argc,argv)
  185. int argc;
  186. char *argv[];
  187. {
  188.     register struct ftpcli *ftp;
  189.  
  190.     ftp = Current->cb.ftp;
  191.     usprintf(ftp->control,"CWD %s\r\n",argv[1]);
  192.     return getresp(ftp->control,200);
  193. }
  194. /* Translate 'mkdir' to 'xmkd' for convenience */
  195. static
  196. int
  197. domkdir(argc,argv)
  198. int argc;
  199. char *argv[];
  200. {
  201.     register struct ftpcli *ftp;
  202.  
  203.     ftp = Current->cb.ftp;
  204.     usprintf(ftp->control,"XMKD %s\r\n",argv[1]);
  205.     return getresp(ftp->control,200);
  206. }
  207. /* Translate 'rmdir' to 'xrmd' for convenience */
  208. static
  209. int
  210. dormdir(argc,argv)
  211. int argc;
  212. char *argv[];
  213. {
  214.     register struct ftpcli *ftp;
  215.  
  216.     ftp = Current->cb.ftp;
  217.     usprintf(ftp->control,"XRMD %s\r\n",argv[1]);
  218.     return getresp(ftp->control,200);
  219. }
  220. static int
  221. dobinary()
  222. {
  223.     char *argv[2];
  224.  
  225.     argv[1] = "i";
  226.     dotype(2,argv);
  227. }
  228. static int
  229. doascii()
  230. {
  231.     char *argv[2];
  232.  
  233.     argv[1] = "a";
  234.     dotype(2,argv);
  235. }
  236.  
  237. /* Handle "type" command from user */
  238. static
  239. int
  240. dotype(argc,argv)
  241. int argc;
  242. char *argv[];
  243. {
  244.     register struct ftpcli *ftp;
  245.     int control;
  246.  
  247.     ftp = Current->cb.ftp;
  248.     control = ftp->control;
  249.     if(argc < 2){
  250.         switch(ftp->type){
  251.         case IMAGE_TYPE:
  252.             printf("Image\n");
  253.             break;
  254.         case ASCII_TYPE:
  255.             printf("Ascii\n");
  256.             break;
  257.         case LOGICAL_TYPE:
  258.             printf("Logical bytesize %u\n",ftp->logbsize);
  259.             break;
  260.         }
  261.         return 0;
  262.     }
  263.     switch(*argv[1]){
  264.     case 'i':
  265.     case 'b':
  266.         ftp->typesent = ftp->type = IMAGE_TYPE;
  267.         usprintf(control,"TYPE I\r\n");
  268.         break;
  269.     case 'a':
  270.         ftp->typesent = ftp->type = ASCII_TYPE;
  271.         usprintf(control,"TYPE A\r\n");
  272.         break;
  273.     case 'l':
  274.         ftp->typesent = ftp->type = LOGICAL_TYPE;
  275.         ftp->logbsize = atoi(argv[2]);
  276.         usprintf(control,"TYPE L %s\r\n",argv[2]);
  277.         break;
  278.     default:
  279.         printf("Invalid type %s\n",argv[1]);
  280.         return 1;
  281.     }
  282.     return getresp(control,200);
  283. }
  284. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  285. static
  286. doget(argc,argv)
  287. int argc;
  288. char *argv[];
  289. {
  290.     char *remotename,*localname;
  291.     register struct ftpcli *ftp;
  292.  
  293.     ftp = Current->cb.ftp;
  294.     if(ftp == NULLFTP){
  295.         printf(Notsess);
  296.         return 1;
  297.     }
  298.     remotename = argv[1];
  299.     if(argc < 3)
  300.         localname = remotename;
  301.     else
  302.         localname = argv[2];
  303.  
  304.     return getsub(ftp,"RETR",remotename,localname);
  305. }
  306. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  307. static
  308. dolist(argc,argv)
  309. int argc;
  310. char *argv[];
  311. {
  312.     char *remotename,*localname;
  313.     register struct ftpcli *ftp;
  314.  
  315.     ftp = Current->cb.ftp;
  316.     if(ftp == NULLFTP){
  317.         printf(Notsess);
  318.         return 1;
  319.     }
  320.     remotename = argv[1];
  321.     if(argc > 2)
  322.         localname = argv[2];
  323.     else
  324.         localname = NULLCHAR;
  325.     return getsub(ftp,"LIST",remotename,localname);
  326. }
  327. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  328. static
  329. dols(argc,argv)
  330. int argc;
  331. char *argv[];
  332. {
  333.     char *remotename,*localname;
  334.     register struct ftpcli *ftp;
  335.  
  336.     ftp = Current->cb.ftp;
  337.     if(ftp == NULLFTP){
  338.         printf(Notsess);
  339.         return 1;
  340.     }
  341.     remotename = argv[1];
  342.     if(argc > 2)
  343.         localname = argv[2];
  344.     else
  345.         localname = NULLCHAR;
  346.     return getsub(ftp,"NLST",remotename,localname);
  347. }
  348. /* Common code to LIST/NLST/RETR */
  349. static int
  350. getsub(ftp,command,remotename,localname)
  351. struct ftpcli *ftp;
  352. char *command,*remotename,*localname;
  353. {
  354.     unsigned long total;
  355.     FILE *fp;
  356.     int cnt,resp,i,control;
  357.     char *mode;
  358.     struct sockaddr_in lsocket;
  359.     int32 startclk,rate;
  360.  
  361.     control = ftp->control;
  362.  
  363.     switch(ftp->type){
  364.     case IMAGE_TYPE:
  365.     case LOGICAL_TYPE:
  366.         mode = Binmode[WRITE_BINARY];
  367.         break;
  368.     case ASCII_TYPE:
  369.         mode = "w";
  370.         break;
  371.     }
  372.     /* Send TYPE message, if necessary */
  373.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
  374.         if(ftp->typesent != ASCII_TYPE){
  375.             /* Directory listings are always in ASCII */
  376.             usprintf(control,"TYPE A\r\n");
  377.             ftp->typesent = ASCII_TYPE;
  378.             resp = getresp(control,200);
  379.             if(resp == -1 || resp > 299){
  380.                 return 1;
  381.             }
  382.         }
  383.     } else if(ftp->typesent != ftp->type){
  384.         switch(ftp->type){
  385.         case ASCII_TYPE:
  386.             usprintf(control,"TYPE A\r\n");
  387.             break;
  388.         case IMAGE_TYPE:
  389.             usprintf(control,"TYPE I\r\n");
  390.             break;
  391.         case LOGICAL_TYPE:
  392.             usprintf(control,"TYPE L %d\r\n",ftp->logbsize);
  393.             break;
  394.         }
  395.         ftp->typesent = ftp->type;
  396.         resp = getresp(control,200);
  397.         if(resp == -1 || resp > 299){
  398.             return 1;
  399.         }
  400.     }
  401.     if(localname == NULLCHAR){
  402.         fp = stdout;
  403.     } else if((fp = fopen(localname,mode)) == NULLFILE){
  404.         printf(Cantwrite,localname);
  405.         return 1;
  406.     }
  407.     /* Open the data connection */
  408.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  409.     listen(ftp->data,0);    /* Accept only one connection */
  410.     ftp->state = RECEIVING_STATE;
  411.  
  412.     /* Send the PORT message and wait for ack */
  413.     i = SOCKSIZE;
  414.     getsockname(ftp->data,(char *)&lsocket,&i);
  415.     sendport(control,&lsocket);
  416.     resp = getresp(control,200);
  417.     if(resp == -1 || resp > 299){
  418.         close_s(ftp->data);
  419.         ftp->data = -1;
  420.         ftp->state = COMMAND_STATE;
  421.         return 1;
  422.     }
  423.     /* Generate the command to start the transfer and wait for ack */
  424.     if(remotename != NULLCHAR)
  425.         usprintf(control,"%s %s\r\n",command,remotename);
  426.     else
  427.         usprintf(control,"%s\r\n",command);
  428.     /* Get the intermediate "150" response */
  429.     resp = getresp(control,100);
  430.     if(resp == -1 || resp >= 400){
  431.         /* Error, quit */
  432.         close_s(ftp->data);
  433.         ftp->data = -1;
  434.         ftp->state = COMMAND_STATE;
  435.         return 1;
  436.     }
  437.     /* Wait for the server to open the data connection */
  438.     cnt = 0;
  439.     ftp->data = accept(ftp->data,NULLCHAR,&cnt);
  440.     startclk = Clock;
  441.  
  442.     total = recvfile(fp,ftp->data,ftp->type);
  443.  
  444. #ifdef    CPM
  445.     if(ftp->type == ASCII_TYPE)
  446.         fputc(CTLZ,fp);
  447. #endif
  448.     startclk = Clock - startclk;
  449.     if(startclk != 0)
  450.         rate = total/startclk;
  451.     else
  452.         rate = 0;
  453.     if(total != -1)
  454.         printf("Get complete: %lu bytes in %lu sec (%lu/sec)\n",
  455.          total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
  456.     else
  457.         printf("Error during data transfer\n");
  458.     if(fp != stdout)
  459.         fclose(fp);
  460.     close_s(ftp->data);
  461.     ftp->data = -1;
  462.     getresp(control,200);
  463.     return 0;
  464. }
  465. /* Send a file. Syntax: put <local name> [<remote name>] */
  466. static
  467. doput(argc,argv)
  468. int argc;
  469. char *argv[];
  470. {
  471.     char *remotename,*localname,*mode;
  472.     register struct ftpcli *ftp;
  473.     int i,resp,control;
  474.     unsigned long total;
  475.     FILE *fp;
  476.     struct sockaddr_in lsocket;
  477.     int32 startclk,rate;
  478.  
  479.     ftp = Current->cb.ftp;
  480.     control = ftp->control;
  481.  
  482.     if(ftp == NULLFTP){
  483.         printf(Notsess);
  484.         return 1;
  485.     }
  486.     localname = argv[1];
  487.     if(argc < 3)
  488.         remotename = localname;
  489.     else
  490.         remotename = argv[2];
  491.  
  492.     if(ftp->type == IMAGE_TYPE)
  493.         mode = Binmode[READ_BINARY];
  494.     else
  495.         mode = "r";
  496.  
  497.     /* Send TYPE message, if necessary */
  498.     if(ftp->typesent != ftp->type){
  499.         switch(ftp->type){
  500.         case ASCII_TYPE:
  501.             usprintf(control,"TYPE A\r\n");
  502.             break;
  503.         case IMAGE_TYPE:
  504.             usprintf(control,"TYPE I\r\n");
  505.             break;
  506.         case LOGICAL_TYPE:
  507.             usprintf(control,"TYPE L %d\r\n",ftp->logbsize);
  508.             break;
  509.         }
  510.         ftp->typesent = ftp->type;
  511.         resp = getresp(control,200);
  512.         if(resp == -1 || resp > 299){
  513.             return 1;
  514.         }
  515.     }
  516.     if((fp = fopen(localname,mode)) == NULLFILE){
  517.         printf(Cantread,localname);
  518.         return 1;
  519.     }
  520.     /* Open the data connection */
  521.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  522.     listen(ftp->data,0);
  523.  
  524.     /* Send the PORT message and wait for ack */
  525.     i = SOCKSIZE;
  526.     getsockname(ftp->data,(char *)&lsocket,&i);
  527.     sendport(control,&lsocket);
  528.     resp = getresp(control,200);
  529.     if(resp == -1 || resp > 299){
  530.         ftp->state = COMMAND_STATE;
  531.         ftp->data = -1;
  532.         return 1;
  533.     }
  534.     /* Generate the command to start the transfer and wait for ack */
  535.     usprintf(control,"STOR %s\r\n",remotename);
  536.     resp = getresp(control,100);
  537.     if(resp == -1 || resp >= 400){
  538.         /* Error, quit */
  539.         ftp->state = COMMAND_STATE;
  540.         ftp->data = -1;
  541.         return 1;
  542.     }
  543.     /* Wait for the data connection to open. Otherwise the first
  544.      * block of data would go out with the SYN, and this may confuse
  545.      * some other TCPs
  546.      */
  547.     accept(ftp->data,NULLCHAR,(int *)NULL);
  548.  
  549.     startclk = Clock;
  550.  
  551.     total = sendfile(fp,ftp->data,ftp->type);
  552.  
  553.     startclk = Clock - startclk;
  554.     if(startclk != 0)
  555.         rate = total/startclk;
  556.     else
  557.         rate = 0;
  558.     if(total != -1)
  559.         printf("Put complete: %lu bytes in %lu sec (%lu/sec)\n",
  560.          total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
  561.     else
  562.         printf("Error during data transfer\n");
  563.  
  564.     fclose(fp);
  565.     close_s(ftp->data);
  566.     ftp->data = -1;
  567.     getresp(control,200);
  568.     return 1;
  569. }
  570. /* Abort a GET or PUT operation in progress. Note: this will leave
  571.  * the partial file on the local or remote system
  572.  */
  573. doabort(argc,argv)
  574. int argc;
  575. char *argv[];
  576. {
  577.     register struct ftpcli *ftp;
  578.  
  579.     if(Current == NULLSESSION || Current->type != FTP){
  580.         printf("Not an active FTP session\n");
  581.         return;
  582.     }
  583.     ftp = Current->cb.ftp;
  584.  
  585.     switch(ftp->state){
  586.     case COMMAND_STATE:
  587.         printf("No active transfer\n");
  588.         return;
  589.     case SENDING_STATE:
  590.         /* Send a premature EOF.
  591.          * Unfortunately we can't just reset the connection
  592.          * since the remote side might end up waiting forever
  593.          * for us to send something.
  594.          */
  595.         shutdown(ftp->data,1);
  596.         break;
  597.     case RECEIVING_STATE:
  598.         /* Just exterminate the data connection; this will
  599.          * generate a RST on the next data packet which will
  600.          * abort the sender
  601.          */
  602.         close_s(ftp->data);
  603.         break;
  604.     }
  605.     ftp->state = COMMAND_STATE;
  606.     ftp->data = -1;
  607.     printf("Transfer aborted\n");
  608. }
  609. /* send PORT message */
  610. static void
  611. sendport(s,socket)
  612. int s;
  613. struct sockaddr_in *socket;
  614. {
  615.     struct mbuf *bp;
  616.  
  617.     /* Compose and send PORT a,a,a,a,p,p message */
  618.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  619.         printf(Nospace);
  620.         return;
  621.     }
  622.     /* I know, this looks gross, but it works! */
  623.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  624.         hibyte(hiword(socket->sin_addr.s_addr)),
  625.         lobyte(hiword(socket->sin_addr.s_addr)),
  626.         hibyte(loword(socket->sin_addr.s_addr)),
  627.         lobyte(loword(socket->sin_addr.s_addr)),
  628.         hibyte(socket->sin_port),
  629.         lobyte(socket->sin_port));
  630.     bp->cnt = strlen(bp->data);
  631.     send_mbuf(s,bp,0,NULLCHAR,0);
  632. }
  633.  
  634. /* Wait for, read and display response from FTP server. Return the result code.
  635.  */
  636. int
  637. getresp(s,mincode)
  638. int s;
  639. int mincode;    /* Keep reading until at least this code comes back */
  640. {
  641.     register char *line;
  642.     int rval;
  643.  
  644.     line = malloc(256);
  645.     for(;;){
  646.         /* Get line */
  647.         if(recvline(s,line,256) == -1){
  648.             rval = -1;
  649.             break;
  650.         }
  651.         rip(line);        /* Remove cr/lf */
  652.         printf("%s\n",line);    /* Display to user */
  653.  
  654.         /* Messages with dashes are continued */
  655.         if(line[3] != '-' && (rval = atoi(line)) >= mincode)
  656.             break;
  657.     }
  658.     free(line);
  659.     return rval;
  660. }
  661.  
  662. struct mbuf *
  663. getline(sp)
  664. struct session *sp;
  665. {
  666.     /* Don't read from the keyboard unless we're the current session */
  667.     while(sp->input == NULLBUF)
  668.         pwait(&sp->input);
  669.     return dequeue(&sp->input);
  670. }
  671. /* Allocate an FTP client control block */
  672. struct ftpcli *
  673. ftp_create()
  674. {
  675.     register struct ftpcli *ftp;
  676.  
  677.     if((ftp = (struct ftpcli *)calloc(1,sizeof (struct ftpcli))) == NULLFTP)
  678.         return NULLFTP;
  679.  
  680.     ftp->state = COMMAND_STATE;
  681.         ftp->type = ASCII_TYPE;        /* Default transfer type */
  682.     ftp->typesent = ASCII_TYPE;    /* Server default is ascii */
  683.     ftp->control = ftp->data = -1;
  684.     return ftp;
  685. }
  686.  
  687.  
  688.